package main

import (
	"context"
	"io"
	"log"
	"net"
	"time"

	pb "github.com/zoujiaqing/smartbox-agent-gateway/src/protocol"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
)

type GatewayClient struct {
	connectionMap map[string]*GatewayConnection
}

type GatewayConnection struct {
	ifaceName      string           // 网卡名
	ipAddr         string           // ipv4 地址
	pingTimeQueue  PingTimeQueue    // Ping 时间保存的容器
	available      bool             // 是否可用
	active         bool             // 是否启用
	grpcConnection *grpc.ClientConn // gRPC 连接对象
}

func main() {
	// 获取本机所有网卡信息
	interfaces, err := net.Interfaces()
	if err != nil {
		log.Fatalf("failed to get network interfaces: %v", err)
	}

	// 创建连接对象映射，以网卡名字为键
	client := &GatewayClient{
		connectionMap: make(map[string]*GatewayConnection),
	}

	// 遍历网卡信息
	for _, iface := range interfaces {
		// 排除回环网卡
		if iface.Flags&net.FlagLoopback != 0 {
			continue
		}

		// 获取网卡地址列表
		addrs, err := iface.Addrs()
		if err != nil {
			log.Printf("failed to get addresses for interface %s: %v", iface.Name, err)
			continue
		}

		// 只处理具有 IPv4 地址的网卡
		hasIPv4 := false
		for _, addr := range addrs {
			if ipNet, ok := addr.(*net.IPNet); ok && ipNet.IP.To4() != nil {
				hasIPv4 = true
				break
			}
		}
		if !hasIPv4 {
			continue
		}

		// 创建连接对象并保存到映射中
		for _, addr := range addrs {
			if ipNet, ok := addr.(*net.IPNet); ok && ipNet.IP.To4() != nil {
				conn, err := client.createConnection(ipNet.IP.String())
				if err != nil {
					log.Printf("failed to create connection for interface %s (%s): %v", iface.Name, ipNet.IP.String(), err)
					continue
				}
				log.Printf("iface.Name is: %s", iface.Name)
				client.connectionMap[iface.Name] = &GatewayConnection{
					ifaceName:      iface.Name,
					ipAddr:         ipNet.IP.String(),
					available:      true,
					active:         false,
					grpcConnection: conn,
				}
				break // 只需处理每个网卡的第一个 IPv4 地址
			}
		}
	}

	// 定期向服务器发送请求
	for _, conn := range client.connectionMap {
		log.Printf("Send requests for iface: %s", conn.ifaceName)
		go client.sendRequests(conn)
	}

	// 无限循环，让程序持续运行
	select {}
}

// 创建连接对象
func (g GatewayClient) createConnection(ip string) (*grpc.ClientConn, error) {
	// 打印网卡信息和 IP 地址
	log.Printf("Creating connection for IP address: %s", ip)

	// 创建一个自定义的 Dialer
	dialer := func(ctx context.Context, addr string) (net.Conn, error) {
		// 创建一个网络地址
		serverAddr, err := net.ResolveTCPAddr("tcp", addr)
		if err != nil {
			return nil, err
		}
		localAddr := &net.TCPAddr{
			IP:   net.ParseIP(ip), // 使用本地网卡 IP 地址
			Port: 0,               // 选择一个可用的端口，或者你也可以指定一个固定的端口
		}
		// 使用指定的 IP 地址连接服务器
		return net.DialTCP("tcp", localAddr, serverAddr)
	}

	conn, err := grpc.DialContext(context.Background(), ":50005", grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithContextDialer(dialer))
	if err != nil {
		return nil, err
	}

	return conn, nil
}

// 向服务器发送请求
func (g GatewayClient) sendRequests(conn *GatewayConnection) {
	for {
		err := g.trySendRequest(conn)
		if err != nil {
			log.Printf("[%s] Send ping failed.", conn.ifaceName)
			// 发送请求失败，等待一段时间后重试
			time.Sleep(1 * time.Second)
		}
	}
}

// 尝试发送请求
func (g GatewayClient) trySendRequest(conn *GatewayConnection) error {
	client := pb.NewGatewayClient(conn.grpcConnection)
	stream, err := client.Ping(context.Background())
	if err != nil {
		log.Printf("open stream error for interface %s: %v", conn.ifaceName, err)
		return err
	}

	// 生成本机时间戳
	startTime := time.Now()
	now := startTime.Unix()
	req := pb.PingRequest{Timestamp: now}
	if err := stream.Send(&req); err != nil {
		log.Printf("can not send for interface %s: %v", conn.ifaceName, err)
		return err
	}
	log.Printf("[%s] %d sent", conn.ifaceName, req.Timestamp)

	// 接收服务器返回的响应
	resp, err := stream.Recv()
	if err == io.EOF {
		log.Printf("[%s] Server closed the stream", conn.ifaceName)
		return nil
	}
	if err != nil {
		log.Printf("[%s] Error receiving response: %v", conn.ifaceName, err)
		return err
	}

	// 计算消耗时间，单位为微秒
	duration := time.Since(startTime).Microseconds()

	// 记录请求响应值和消耗时间
	g.recordResponseInfo(conn, resp.Timestamp, duration)

	time.Sleep(time.Second * 1)

	return nil
}

// 记录服务器返回的值和请求的消耗时间
func (g GatewayClient) recordResponseInfo(conn *GatewayConnection, timestamp int64, duration int64) {
	// 将 Unix 时间戳转换为时间格式
	responseTime := time.Unix(timestamp, 0).Format("2006-01-02 15:04:05")
	log.Printf("[%s] Received response at %s, took %d microseconds", conn.ifaceName, responseTime, duration)

	conn.pingTimeQueue.Enqueue(duration)
	log.Printf("Ping Value: %f", conn.pingTimeQueue.Average()/1000)
}
